# include <sys/socket.h>
# include <netinet/in.h>
# include <stdio.h>
# include <netdb.h>
# include <string.h>
# include <sys/types.h>
# include <unistd.h>
# include <math.h>
# include <stdlib.h>
# include <sys/stat.h>
# include <errno.h>
# include <fcntl.h>
# include <sys/time.h>

# include <pthread.h>

# include <string>
# include <vector>

# define TCP_PORT 31038
# define MAXSIZE 50
# define BACKLOG 10

# define IDLE -1
# define START_WITH_URL 0
# define START 1
# define STOP 2

using namespace std;

const char localHostIp[10] = "127.0.0.1";

int sockfd, ret, threadID;
struct sockaddr_in ser_addr;
struct in_addr **addrs;
hostent* sh;

vector<string> url;
vector<string> hardcodeURL;

pthread_t worker;

bool establishConnection()
{
	int sockfd, con_fd, ret;
	struct sockaddr_in my_addr;
	struct sockaddr_in their_addr;
	int sin_size;

	sockfd = socket(AF_INET, SOCK_STREAM, 0);          //create socket
	if (sockfd <0)
	{
		printf("error in socket!");
		return false;
	}
	
	my_addr.sin_family = AF_INET;
	my_addr.sin_port = htons(TCP_PORT);
	my_addr.sin_addr.s_addr = htonl(INADDR_ANY);//inet_addr("172.0.0.1");
	bzero(&(my_addr.sin_zero), 8);
	ret = bind(sockfd, (struct sockaddr *) &my_addr, sizeof(struct sockaddr));
	if (ret <0)
	{
		printf("error in binding");
		return false;
	}
	
	ret = listen(sockfd, BACKLOG);
	if (ret <0) {
		printf("error in listening");
		return false;
	}

	fprintf(stdout, "Server steup.\n");
	return true;
}

void disposeConnection()
{
	close(sockfd);
}

unsigned char getNextByte()
{
	unsigned char x;

	int n = recv(sockfd, &x, 1, 0);

	return x;
}

string getNextString()
{
	unsigned char buf = getNextByte();
	string str = "";

	while (buf != '\n')
	{
		str += buf;
		buf = getNextByte();
	}

	return str;
}

void sendString(string str)
{
	send(sockfd, str.c_str(), str.length(), 0);
}

char getInstruction()
{
	return getNextByte();
}

void debugURL()
{
	for (int i = 0; i < url.size(); i ++)
	{
		fprintf(stdout, "%s\n", url[i].c_str());
	}
}

void *crawl(void * param)
{
	for (int i = 0; i < hardcodeURL.size(); i ++)
	{
		sendString(hardcodeURL[i]);
		sleep(1);
	}

	pthread_exit(NULL);
}

void startCrawling()
{
	pthread_create(&worker, NULL, crawl, NULL);
}

int main()
{
	char opcode;
	char totalURL;
	int curState = IDLE;

	hardcodeURL.push_back("www.google.com");
	hardcodeURL.push_back("www.nus.edu.sg");
	hardcodeURL.push_back("www.comp.nus.edu.sg");
	hardcodeURL.push_back("www.stackoverflow.com");
	hardcodeURL.push_back("www.sina.com");
	hardcodeURL.push_back("cppreference.com");

	if (establishConnection())
	{
		while (curState != STOP)
		{
			opcode = getInstruction();

			switch (opcode)
			{
				case START_WITH_URL:
					fprintf(stdout, "Start with URL:\n");
					url.clear();
					totalURL = getNextByte();

					while (totalURL --)
					{
						url.push_back(getNextString());
						fprintf(stdout, "%s\n", url.back().c_str());
					}

					curState = START_WITH_URL;
					startCrawling();
					break;
				case START:
					curState = START;
					startCrawling();
					break;
				case STOP:
					fprintf(stdout, "Stop\n");
					curState = STOP;
					pthread_cancel(worker);
					break;
			}
		}

		disposeConnection();
	}
}